using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Deepcode.Flux.Core.Collections;
using Deepcode.Flux.Core.Services.Errors;
using Deepcode.Flux.Core.Services.FileSystem;
using Deepcode.Flux.Core.Services.Configuration;
using Deepcode.Flux.Core.UI.Interfaces;
using System.Collections.Specialized;
using Deepcode.Flux.Core.Systems.Addins;
using Deepcode.Flux.Core.Systems.Security;

public partial class Setup_Install_do_install : Deepcode.Flux.Core.UI.NonCachePage
{
	#region Page_Load
	protected void Page_Load(object sender, EventArgs e)
	{
		bool bFail = false;
		// Ensure we have configuration
		if( SetupDictionarySession.Config.ToArray().Length < 1 ) bFail = true;
		// Ensure we're not already configured
		if( System.IO.File.Exists( Server.MapPath( "~/settings/Flux.config" ))) bFail = true;
		if (bFail)
		{
			ErrorService.Service().DoError(StandardError.ALREADY_CONFIGURED);
			return;
		}

		// Begin the installation
		ProcessInstallSession();
	}
	#endregion
	#region ProcessInstallSession()
	protected void ProcessInstallSession()
	{
		panels.BeginEndItem("Starting installation of core components", Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.OK);
		SqlConnection.ClearAllPools();
		
		// 1.0 Write configuration to Flux.config
		if( ! CreateConfigFile() ) return;
		// 2.0 Create the database - wait for up to 60 seconds to see if it's ready
		if (!CreateDatabase())
		{
			DeleteConfigFile();
			return;
		}
		DatabaseLatencyWait();
		// 3.0 Run all of the sql's in ./sql
		if (!InstallDatabase())
		{
			DropDatabase();
			DeleteConfigFile();
			return;
		}
		// 4.0 Create a super user
		if (!CreateSuperUser())
		{
			DropDatabase();
			DeleteConfigFile();
			return;
		}
		// 5.0 Set version stamp
		if (!SetVersionStamp())
		{
			DropDatabase();
			DeleteConfigFile();
			return;
		}
		// 6.0 If we're forms auth - authenticate user now
		if (SetupDictionarySession.Config["FluxAuthMode"] == "FORMS")
		{
			panels.BeginItem("Forms authenticating super user");
			string em = SetupDictionarySession.Config["FluxAuth_Email"];
			System.Web.Security.FormsAuthentication.SetAuthCookie(em, false);
			panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.OK);
		}
		// Finished
		panels.FinishedUrl = "~/admin";
		panels.Success();
	}
	#endregion
	#region 1.0 Config File Management
	/// <summary>
	/// Responsible for creating the configuration file as necessary
	/// </summary>
	/// <returns></returns>
	protected bool CreateConfigFile()
	{
		// Ensure the path exists
		panels.BeginItem("Create /settings");
		PathCheckService pcs = new PathCheckService();
		if (!pcs.PathCreate(Server.MapPath("~/settings")))
		{
			panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.FAIL);
			return false;
		}
		panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.OK);

		// Create the property file and write up the settings
		panels.BeginItem("Writing /settings/Flux.config");
		try
		{
			PropertyFile pf = new PropertyFile("~/settings/Flux.config");
			pf["Authentication"] = SetupDictionarySession.Config["FluxAuthMode"];
			pf["ServerDSN"] = SetupDictionarySession.GetConnectionString(false);
			pf["MainDSN"] = SetupDictionarySession.GetConnectionString(true);
			pf["MailServer"] = SetupDictionarySession.Config["DefaultMailServer"];
			pf["SystemEmail"] = SetupDictionarySession.Config["DefaultFromAddress"];
			pf["ClientName"] = SetupDictionarySession.Config["ClientName"];
			pf.Save();
			panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.OK);
		}
		catch (Exception e)
		{
			panels.Error(e);
			return false;
		}
		
		
		return true;
	}
	/// <summary>
	/// Removes the configuration file
	/// </summary>
	protected void DeleteConfigFile()
	{
		panels.BeginItem("Removing configuration file");
		try
		{
			System.IO.File.Delete(Server.MapPath("~/settings/Flux.config"));
		}
		catch
		{
			panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.FAIL);
			return;
		}
		panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.OK);
	}
	#endregion
	#region 2.0 Database Management
	/// <summary>
	/// Create the database in SQL Server
	/// </summary>
	/// <returns></returns>
	protected bool CreateDatabase()
	{
		if (SetupDictionarySession.Config["SQLDBExists"] == "YES") return true;
		panels.BeginItem("Creating database");

		// First, try to connect using the full DSN. If that works, we can't create, it already exists!
		SqlConnection c = null;
		try
		{
			c = new SqlConnection(SetupDictionarySession.GetConnectionString(true));
			c.Open();
			c.Close();

			// If we get this far, the database already exists, so error it
			panels.Error(new Exception("Database already exists"));
			return false;
		}
		catch
		{
			if (c != null) c.Close();
		}

		// Create the database and alter it to support read_committed_snapshot isolation level
		try
		{
			c = new SqlConnection(SetupDictionarySession.GetConnectionString(false));
			string sql = "CREATE DATABASE " + SetupDictionarySession.Config["SQLServerDatabase"];
			string sql2 = "ALTER DATABASE " + SetupDictionarySession.Config["SQLServerDatabase"] + " SET READ_COMMITTED_SNAPSHOT ON;";

			c.Open();
			SqlCommand cmd = new SqlCommand(sql, c);
			cmd.ExecuteNonQuery();
			SqlCommand cmd2 = new SqlCommand(sql2, c);
			cmd2.ExecuteNonQuery();
			c.Close();

			panels.EndItem( Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.OK );
			return true;
		}
		catch (Exception ex)
		{
			if (c != null) c.Close();
			panels.Error( ex );
			return false;
		}
	}
	/// <summary>
	/// Because there is latency between a CREATE DATABASE command being issued and
	/// SQL Server bringing that database online and available for ADO, this function
	/// will pause for up to 60 seconds, checking every second if the database is available
	/// and online.
	/// </summary>
	private void DatabaseLatencyWait()
	{
		// There is latency between the CREATE DATABASE command and SQL Server
		// actually bringing the database online and making it available to ADO, 
		// therefore the following loop will
		// wait for up to 60 seconds to ensure that the database comes online. 
		DateTime TimeoutTime = DateTime.Now.AddSeconds(60);
		while (DateTime.Now < TimeoutTime)
		{
			bool bOpened = false;

			SqlConnection c = null;
			try
			{
				c = new SqlConnection(SetupDictionarySession.GetConnectionString(true));
				c.Open();
				bOpened = true;
			}
			catch
			{
				bOpened = false;
			}
			finally
			{
				if (c != null) c.Close();
			}
			c.Dispose();

			if (bOpened) break;
			// Pause for 1 second before continuing to try again
			DateTime PauseTime = DateTime.Now.AddSeconds(1);
			while (DateTime.Now < PauseTime) { }
		}
	}
	/// <summary>
	/// Functionality to dispose of the database in an error condition
	/// </summary>
	private void DropDatabase()
	{
		// If the database was flagged as being already existing, don't drop it!
		if (SetupDictionarySession.Config["SQLDBExists"] == "YES")
		{
			panels.BeginEndItem("As the database already existed, you must now remove tables from the database manually and re-run setup", Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.NOSTATE);
			return;
		}
		
		panels.BeginItem("Dropping the database");
		try
		{
			// Attempt to connect using the SQL Server only dsn
			SqlConnection c = new SqlConnection(SetupDictionarySession.GetConnectionString(false));
			string sql = "DROP DATABASE " + SetupDictionarySession.Config["SQLServerDatabase"];

			c.Open();
			SqlCommand cmd = new SqlCommand(sql, c);
			cmd.ExecuteNonQuery();
			c.Close();

			panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.OK);
		}
		catch
		{
			panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.FAIL);
		}
	}
	#endregion
	#region 3.0 SQL Script processing
	/// <summary>
	/// Processes all of the SQL installation scripts
	/// </summary>
	/// <returns></returns>
	protected bool InstallDatabase()
	{
		SqlConnection c = null;
		SqlTransaction t = null;
		try
		{
			StringCollection SQLFiles = GetSQLBatches();
			c = GetConnection();
			t = c.BeginTransaction();

			Deepcode.Flux.Core.Data.SQLBatchProcessor batch = new Deepcode.Flux.Core.Data.SQLBatchProcessor();
			foreach( string filename in SQLFiles )
			{
				
				panels.BeginItem("&nbsp;&nbsp;&nbsp;SQL: " + System.IO.Path.GetFileName( filename ));
				batch.ExecuteQueryScriptSQLFile( filename, c, t );
				panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.OK);
			}

			t.Commit();
			c.Close();
		}
		catch (Exception e)
		{
			if (t != null) t.Rollback();
			if (c != null) c.Close();
			
			panels.Error( e );
			return false;
		}
		return true;
	}
	/// <summary>
	/// Gets all of the SQL files available for processing
	/// </summary>
	/// <returns></returns>
	private StringCollection GetSQLBatches()
	{
		panels.BeginItem("Searching for SQL Scripts");
		
		Deepcode.Flux.Core.Services.FileSystem.FileScanService FileScan = new FileScanService();
		
		StringCollection SQLFiles = FileScan.SearchFilesInWebPath("~/admin/Setup/Install/sql/", "*.sql", true);
		
		if( SQLFiles.Count < 1 ) throw new Exception("No SQL Scripts to process!");
		
		panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.OK);

		return SQLFiles;
	}
	/// <summary>
	/// Get a connection to the database
	/// </summary>
	/// <returns></returns>
	private SqlConnection GetConnection()
	{
		panels.BeginItem("Opening database");
		SqlConnection result = new SqlConnection(SetupDictionarySession.GetConnectionString(true));
		result.Open();
		panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.OK);
		return result;
	}
	#endregion
	#region 4.0 Super user creation
	/// <summary>
	/// Create the super user
	/// </summary>
	/// <returns></returns>
	protected bool CreateSuperUser()
	{
		panels.BeginItem("Creating super user");
		try
		{
			Deepcode.Flux.Core.Systems.Security.AdministrationUser superuser = new Deepcode.Flux.Core.Systems.Security.AdministrationUser();
			superuser.EMailAddress = SetupDictionarySession.Config["FluxAuth_Email"];
			if (SetupDictionarySession.Config["FluxAuthMode"] == "WINDOWS")
				superuser.NT_Username = SetupDictionarySession.Config["FluxAuth_WA_Username"];

			superuser.RealName = "Superuser created during setup";
			superuser.IsSuperUser = true;

			AdministrationUsers u = new AdministrationUsers();
			u.Save(superuser);
			
			if (SetupDictionarySession.Config["FluxAuthMode"] != "WINDOWS")
				u.SetPassword(superuser.PK_ID, SetupDictionarySession.Config["FluxAuth_FA_Password"]);

			panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.OK);
		}
		catch (Exception ex)
		{
			panels.Error(ex);
			return false;
		}
		return true;
	}
	#endregion
	#region 5.0 Set version stamp
	protected bool SetVersionStamp()
	{
		panels.BeginItem("Setting version in Flux datastore");
		try
		{
			Deepcode.Flux.Core.Systems.Addins.KnownAddins ka = new Deepcode.Flux.Core.Systems.Addins.KnownAddins();
			ka.SetDbVersionFor( Deepcode.Flux.Core.Services.Information.Guids.FluxCoreGuid, "1.0.0" );
			panels.EndItem(Deepcode.Flux.Core.UI.Controls.Special.UpdatePanels.States.OK);
		}
		catch (Exception e)
		{
			panels.Error( e );
			return false;
		}
		return true;
	}
	#endregion
}
